package com.zxl.admin.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.SaTokenException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.zxl.common.Result;
import com.zxl.common.constant.ResponseStatusCode;
import com.zxl.common.exception.AuthenticationException;
import com.zxl.common.exception.BusinessException;
import com.zxl.common.exception.LoginException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.Date;
import java.util.List;

/**
 * @author zxw
 * @date 2023/3/31
 */
@ControllerAdvice
@Slf4j
@Data
public class GlobalExceptionAdvice {

    private static final String includeUri = "/favicon.ico";

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Result<String> exception(HttpServletRequest request,HttpServletResponse response,Exception ex){
        log.error("Unexpected error in ", ex);
        Result<String> result;
        if (ex instanceof MethodArgumentNotValidException){
            String defaultMessage = ((MethodArgumentNotValidException) ex).getBindingResult().getFieldError().getDefaultMessage();
            result = new Result<>(ResponseStatusCode.PARAM_ERROR.getCode(),defaultMessage);
        }else if(ex instanceof BindException){
            result = bindException(request,ex);
        }else if(ex instanceof InvalidParameterException){
            result = invalidParameterException(request,ex);
        }else if(ex instanceof AuthenticationException){
            result = authenticationException(request,ex);
        }else if(ex instanceof SaTokenException){
            result = saTokenException(request,ex);
        }else if(ex instanceof NoHandlerFoundException){
            result = noHandlerFoundException(request,response,ex);
        }else if(ex instanceof BusinessException) {
            Integer code = ((BusinessException) ex).getCode();
            result = new Result<>(code, ex.getMessage());
        }else if(ex instanceof HttpMessageNotReadableException){
            result = notReadableException((HttpMessageNotReadableException)ex);
        }else if(ex instanceof MaxUploadSizeExceededException) {
            result = new Result<>(ResponseStatusCode.ERROR.getCode(), "文件大小不能超过5MB");
        } else if (ex instanceof IOException){
            if(ex.getMessage()!=null && ex.getMessage().contains("OLE2") || ex.getMessage().contains("OOXML")){
                result = Result.fail("文件格式错误");
            }else{
                result = Result.fail();
            }
        } else{
            result = Result.fail();
        }

        return result;
    }

    /**
     * 处理参数解析异常
     *
     * @param e
     * @return
     */
    public Result<String> notReadableException(HttpMessageNotReadableException e) {
        Result<String> result;
        String errorMessage = null;

        //解析具体参数名
        Throwable rootCause = e.getRootCause();
        if (rootCause instanceof JsonMappingException) {
            List<JsonMappingException.Reference> path = ((JsonMappingException) rootCause).getPath();
            if (!CollectionUtils.isEmpty(path)) {
                //只取最后一个参数
                for (int i = path.size() - 1; i >= 0; i--) {
                    JsonMappingException.Reference reference = path.get(i);
                    if (reference == null) {
                        continue;
                    }
                    errorMessage = reference.getFieldName();
                    break;
                }
            }
        }

        //解析不出具体参数名就直接返回message
        if (errorMessage == null) {
            errorMessage = e.getMessage();
        }

        result = new Result<>(ResponseStatusCode.PARAM_ERROR);
        result.setData(errorMessage);
        return result;
    }

    /**
     * 捕捉spring请求参数绑定异常
     * @param ex
     * @return
     */
    public Result<String> bindException(HttpServletRequest request, Exception ex) {
        Result<String> result = new Result<>();
        if(ex instanceof BindException){
            BindException bindException = (BindException) ex;
            BindingResult bindingResult = bindException.getBindingResult();
            if(bindingResult!=null){
                String field = bindingResult.getFieldError().getField();
                if(StringUtils.isNotBlank(field)){
                    result.setData(field);
                }
            }
        }
        result.setCodeAndMessage(ResponseStatusCode.PARAM_ERROR);
        result.setSuccess(false);
        return result;
    }

    /**
     * 无效参数拦截处理
     * @param ex
     * @return
     */
    public Result<String> invalidParameterException(HttpServletRequest request,Exception ex){
        if(ex instanceof InvalidParameterException){
            String message = ex.getMessage();
            return new Result<>(ResponseStatusCode.ERROR.getCode(),message+"参数错误");
        }
        return new Result<>(ResponseStatusCode.ERROR.getCode(),"参数错误");
    }

    /**
     * 第三方接口权限异常
     * @param ex
     * @return
     */
    public Result<String> authenticationException(HttpServletRequest request,Exception ex){
        if(ex instanceof AuthenticationException){
            String message = ex.getMessage();
            return new Result<>(ResponseStatusCode.ERROR.getCode(),message);
        }
        return new Result<>(ResponseStatusCode.ERROR.getCode(),"请求失败");
    }


    /**
     * 权限异常拦截
     * @param ex
     * @return
     */
    public Result<String> saTokenException(HttpServletRequest request,Exception ex) {
        // 不同异常返回不同状态码
        Result result = null;
        // 如果是未登录异常
        if (ex instanceof NotLoginException) {
            result = new Result(ResponseStatusCode.PERMISSION_NOT_LOG_IN);
            // 如果是角色异常
        } else if(ex instanceof NotRoleException) {
            NotRoleException ee = (NotRoleException) ex;
            result = new Result(ResponseStatusCode.PERMISSION_NOT_LOG_IN.getCode(),"无此角色：" + ee.getRole());
            // 如果是权限异常
        } else if(ex instanceof NotPermissionException) {
            NotPermissionException ee = (NotPermissionException) ex;
            result =new Result(ResponseStatusCode.PERMISSION_NOT_HAVE.getCode(),"无此权限：" + ee.getCode());
            // 如果是被封禁异常
        } else if(ex instanceof LoginException){
            result = new Result(ResponseStatusCode.PERMISSION_NOT_LOG_IN.getCode(),ex.getMessage());
        }
        return result;
    }

    /**
     * 404错误拦截
     * @param ex
     * @param request
     * @param response
     * @return
     */
    public Result<String> noHandlerFoundException( HttpServletRequest request, HttpServletResponse response,Exception ex){
        return new Result<>(ResponseStatusCode.NOT_FOUND_ERROR);
    }


}
